package com.fusion.misc;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Random;

import org.w3c.dom.Document;

import com.fusion.core.settings.Friend;
import com.fusion.core.settings.Internal;
import com.fusion.core.settings.My;
import com.fusion.core.settings.Server;
import com.fusion.core.settings.Settings;
import com.fusion.core.settings.Share;
import com.stendahls.nif.util.SXML;
import com.stendahls.nif.util.xmlserializer.XMLSerializer;


/**
 * @author SwethaShivakumar 
 * Project: fusion
 * Date: Mar 18, 2009
 * URL: http://fusionwiki.co.cc
 * Mail Me At: shiva@fusionproject.co.cc
 */
public class GenerateTestSuite {
    private ArrayList<Friend> users = new ArrayList<Friend>();
    private HashMap<Friend, Settings> settings = new HashMap<Friend, Settings>();
    private HashSet<Friend> usedFriends = new HashSet<Friend>();
    private HashMap<Friend, Friend> parentFor = new HashMap<Friend, Friend>();

    private static final int N_FRIENDS_FACTOR = 2;
    private String shareDirectory = "C:/share";  // (conditional defaults) added by (Shiva) Refactoring @ 11:04 PM 3/23/2009
    private static final boolean LINK_MORE_THEN_JUST_TO_SWETHA = true;

    public GenerateTestSuite(String shareDirectory) throws Exception {
//    	New lines (conditional defaults) added by (Shiva) Refactoring @ 10:53 PM 3/23/2009
    	if(shareDirectory != null){
    		this.shareDirectory = shareDirectory;
    	} else {
    		this.shareDirectory = "C:/share"; 
    	}
        
        if (!new File(this.shareDirectory).exists()) {
            System.out.println("Directory with fake share test data does not exist: "+shareDirectory+".");
            System.exit(1);
        }

        System.out.println("Cleaning up old testsuite...");
        File settingsFile = new File("testsuite/settings");
        settingsFile.mkdirs();
        new File("testsuite/logs").mkdirs();

        for(File f : settingsFile.listFiles()) f.delete();

        System.out.println("Setting up Swetha...");
        Friend swetha = new Friend("Swetha", "localhost", 143, 10333);
        Settings swethaSettings = createSettings(swetha);
        settings.put(swetha, swethaSettings);

        System.out.println("Creating users...");
        int port = 100;
        Random r = new Random();
        for(String nick : usernames) {
            if (port == 135) port+=10;
            Friend f = new Friend(nick, "localhost", r.nextInt(), port++);
            users.add(f);
        }

        System.out.println("Creating settings for users...");
        for(Friend f : users) {
            Settings s = createSettings(f);
            settings.put(f, s);
        }

        System.out.println("Linking friends to Swetha...");
        for(int i=0;i<N_FRIENDS_FACTOR/2;i++) {
            Friend f = getNewFriend();
            link(swetha, f);
        }

        if (LINK_MORE_THEN_JUST_TO_SWETHA) {
            System.out.println("Linking friends to Swetha friends (recursive)...");
            for(int i=0;i<swethaSettings.getFriendlist().size();i++) {
                Friend f = swethaSettings.getFriendlist().get(i);
                addFriends(f, 1);
            }
        }

        saveSettings("settings/", swethaSettings);

        int n = 0;
        System.out.println("Saving settings...");
        for(Friend f : usedFriends) {
            saveSettings("settings/", settings.get(f));
            n++;
        }
        System.out.println("Saved "+n+" users excl Swetha.");
    }

    private void addFriends(Friend friend, int level) {
        if (level > 3) return;
        System.out.println("Adding friends for "+friend);
        for(int i=0;i<N_FRIENDS_FACTOR;i++) {
            if (Math.random() > 0.85) {
                Friend f = getUsedFriend();
                if (f != null) {
                    System.out.println("  adding USED friend: "+f);
                    link(friend, f);
                }
            } else if (Math.random() > 0.7 || getFriendFromParentOf(friend) == null) {
                Friend f = getNewFriend();
                System.out.println("  adding random friend: "+f);
                link(friend, f);
                addFriends(f, level+1);
            } else {
                Friend f = getFriendFromParentOf(friend);
                System.out.println(" Adding known friend: "+f);
                link(friend, f);
            }
        }
    }

    private Friend getUsedFriend() {
        List<Object> l = Arrays.asList(usedFriends.toArray());
        if (l.size() == 0) return null;
        return (Friend)l.get((int)(l.size()*Math.random()));
    }

    private Friend getFriendFromParentOf(Friend friend) {
        Settings parent = settings.get(parentFor.get(friend));
        ArrayList<Friend> al = parent.getFriendlist();
        boolean ok=false;
        for(Friend f : al) {
            if (!settings.get(friend).hasFriend(f) && f != friend) {
                ok = true;
                break;
            }
        }
        if (!ok) return null;
        for(;;) {
            Friend f = al.get((int)(Math.random()*al.size()));
            if (!settings.get(friend).hasFriend(f) && f != friend) {
                return f;
            }
        }
    }

    private void link(Friend f1, Friend f2) {
        if (f1 == f2) return;
        Settings s1 = settings.get(f1);
        Settings s2 = settings.get(f2);
        s1.addFriend(f2);
        s2.addFriend(f1);
        parentFor.put(f2, f1);
    }

    private Friend getNewFriend() {
        if (usedFriends.size() == users.size()) {
            System.err.println("All friends used!");
            return null;
        }
        for(;;) {
            Friend f = users.get((int)(Math.random()*(users.size())));
            if (!usedFriends.contains(f)) {
                usedFriends.add(f);
                return f;
            }
        }
    }

    private void saveSettings(String path, Settings settings) throws Exception {
        XMLSerializer s = new XMLSerializer();
        Document doc = s.serialize(settings);
        FileOutputStream out = new FileOutputStream(new File("testsuite/"+path+settings.getMy().getNickname()+".xml"));
        out.write(SXML.toString(doc).getBytes());
        out.flush();
        out.close();
    }

    private Settings createSettings(Friend user) {
        Settings s = new Settings();
        s.setInternal(new Internal(120));
        s.setMy(new My(user.getGuid(), user.getNickname()));
        s.setServer(new Server(user.getPort()));
        String path = "testsuite/data/"+user.getNickname()+"/";
        s.getInternal().setFiledatabasefile(path+"share.dat");
        s.getInternal().setFiledatabaseindexfile(path+"share.idx");
        s.getInternal().setDownloadquefile(path+"downloads.dat");
        s.getInternal().setCorestatefile(path+"core.dat");

        s.getInternal().setDownloadfolder(path+"downloads");
        s.getInternal().setCachefolder(path+"cache");
        s.getInternal().setKeystorefilename(path+"me.ks");

        if (Math.random() > 0.5)
            s.getMy().createChecksumAndSetInvitations((int)(4*Math.random()));
        else
            s.getMy().setInvitations(0);
        s.addShare(getRandomShare());
        return s;
    }

    private Share getRandomShare() {
        File files[] = new File(shareDirectory).listFiles();
        return new Share(files[((int)(Math.random()*files.length))].getPath());
    }

    public static void main(String[] args) throws Exception {
    	//New line added by (Shiva) Refactoring @ 10:10 PM 3/23/2009
    	String sharePath = null;
    	if (args.length != 1) {        	
            System.out.println("First argument must be path to files used for shares for test users.");
            //New lines added by (Shiva) @ 10:10 PM 3/23/2009
            System.out.println("No Share path specified Using Default path: C:\\share");            
//            return;
        } else {
        	sharePath = args[0];
        }
        new GenerateTestSuite(sharePath);
    }

    private String[] usernames = {"Swetha", "SuSwetha", "Shiva", "Shivakumar", "Sandeep", "Lalli", "subbu",
    		"lalithamma", "Subrahmanyam", "lalliSubbu", "Bujji", "Raja", "Ram", "Divya", "Madhu", "Sravanthi",
    		"Narmadha", "Teja", "Mdhavi", "Noor", "Shashank", "Shankar", "Raj", "Chandan", "Ramreddy", "Appu",
    		"Bharadwaj", "Anil", "Prabha", "Satish"
    };

    
}
